package dao

import (
	"context"
	"encoding/json"
	"fmt"

	els "mxshop_srvs/goods_srv/dao/elasticsearch"
	"mxshop_srvs/goods_srv/dao/mysql"
	"mxshop_srvs/goods_srv/model"
	proto "mxshop_srvs/goods_srv/proto/gen/v1"
	"mxshop_srvs/shared/es"
)

type GoodsRepo interface {
	GetGoodsList(ctx context.Context, req *proto.GoodsFilterRequest, categoryLevel int32) (count int32, goods []model.Goods, err error)
}

type goodsRepo struct {
	search els.GoodsSearch
	dao    mysql.GoodsDAO
}

func NewGoodsRepo(dao mysql.GoodsDAO, search els.GoodsSearch) GoodsRepo {
	return &goodsRepo{
		dao:    dao,
		search: search,
	}
}

type Goods struct {
	ID int32 `json:"id"`
}

func (g *goodsRepo) GetGoodsList(ctx context.Context, req *proto.GoodsFilterRequest, categoryLevel int32) (count int32, goods []model.Goods, err error) {
	q := es.NewBoolQuery()
	rangeQ := es.NewRangeQuery("shop_price")

	// 使用must是会计算分数权重的
	if req.KeyWords != "" {
		q = q.Must(es.NewMultiMatchQuery(req.KeyWords, "name", "goods_brief").H)
	}

	// 而filter只是过滤并不会影响分数的权重,在这里使用filter更合适
	if req.IsHot {
		q = q.Filter(es.NewTermQuery("is_hot", req.IsHot).H)
	}
	if req.IsNew {
		q = q.Filter(es.NewTermQuery("is_new", req.IsNew).H)
	}

	if req.PriceMin > 0 {
		q = q.Filter(rangeQ.Gte(req.PriceMin).H)
	}
	if req.PriceMax > 0 {
		q = q.Filter(rangeQ.Lte(req.PriceMax).H)
	}

	if req.Brand > 0 {
		q = q.Filter(es.NewTermsQuery("brands_id", req.Brand).H)
	}

	if req.TopCategory > 0 {
		// 获取选择分类的所有CategoryID
		categoryIDs, err := g.dao.GetCategoryIDs(req.TopCategory, categoryLevel)
		if err != nil {
			fmt.Printf("%v\n", err)
			return 0, nil, err
		}

		q = q.Filter(es.NewTermsQuery("category_id", categoryIDs...).H)
	}

	// 分页
	// PS:注意，elasticsearch的from是类似索引的存在，从0开始。不可以直接当成Gorm的分页来使用。
	var from, size int
	switch {
	case req.PagePerNums > 100:
		size = 100
	case req.PagePerNums <= 0:
		size = 10
	default:
		size = int(req.PagePerNums)
	}
	if req.Pages > 0 {
		from = int(req.Pages-1) * size
	}
	q.From(from).Size(size)

	hit, err := g.search.Query(ctx, model.ESGoods{}.GetIndexName(), q.H)
	if err != nil {
		return 0, goods, err
	}

	count = int32(hit.Total())
	if count == 0 {
		return 0, nil, els.NotFound
	}

	hits := hit.Hits()

	goodsIDs := make([]int32, 0, len(hits))
	for _, hit := range hits {
		var goods Goods

		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		marshal, err := json.Marshal(source)
		if err != nil {
			return 0, nil, err
		}

		err = json.Unmarshal(marshal, &goods)
		if err != nil {
			return 0, nil, err
		}

		goodsIDs = append(goodsIDs, goods.ID)
	}

	goods, err = g.dao.GetGoodsList(goodsIDs)
	if err != nil {
		return 0, nil, err
	}

	return count, goods, nil
}
